<?php

namespace App\Cache;

use App\Utils\CacheLists;
use Cache;
use App\Cache\CommonCache;
use Log;

class BaseCache {

    public $method = 'get';
    public $cacheName = null;
    public $cachePar = array();
    public $cacheValue = null;
    public $USER_PRE = 'joymedia';
    protected $CACHE_BREAKDOWN_TIME = 0.1;//缓存击穿情况6s缓存
    protected $RANDOM_NUM = 100;
    public function __construct(){
        $this->USER_PRE = env('USER_PRE','joymedia');
    }
    //解析
    public function HandleCache() {
        $cacheList = with(new CacheLists())->lists;
        if (!isset($cacheList[$this->cacheName])) {
            return 'NO_CACHE';
        }
        $object = $cacheList[$this->cacheName];
        if(isset($object['SELF_PRE'])&&$object['SELF_PRE']){
            $this->USER_PRE = $this->USER_PRE.$object['SELF_PRE'];
        }
        //找出tag，key
        $cacheArr['type'] = $object['type'];
        $cacheArr['tag'] = array();
        foreach ($object['tag'] as $key => $tag) {
            for ($i = 0; $i < count($this->cachePar); $i++) {
                $tag = str_replace('#' . $i . $i . '#', $this->cachePar[$i], $tag);
            }
            $cacheArr['tag'][] = $tag; //缓存tag
        }
        $cacheArr['key'] = $object['key']; //缓存key
        for ($i = 0; $i < count($this->cachePar); $i++) {
            $cacheArr['key'] = str_replace('#' . $i . $i . '#', $this->cachePar[$i], $cacheArr['key']);
        }
        $cacheTime = with(new CacheLists())->time;
        $cacheArr['time'] = isset($cacheTime[$this->cacheName]) ? $cacheTime[$this->cacheName] : $object['time']; //缓存时间
        if(isset($object['is_random_time']) && $object['is_random_time']){
            $cacheArr['time'] = $cacheArr['time'] + rand(0,$cacheArr['time']*$this->RANDOM_NUM)/100;
        }
        if(isset($this->cacheValue->APIENV)){
            unset($this->cacheValue->APIENV);
        }
        $cacheArr['value'] = $this->cacheValue;
        $retCache = null; // 
        //get-pull等函数是否存在
        if ($this->method == 'has' || $this->method == 'put') {
            $retCache = $this->{$this->method}($cacheArr, $object['before']);
            if( $this->method == 'put' && $cacheArr['value'] !== 'EMPTY_CACHE'){
                $index = $cacheArr;
                $index['key'] = $cacheArr['key'].'_bat';
                $index['time'] = 8000;
                $this->{$this->method}($index);
            }
        } else if ($this->method == 'get' || $this->method == 'pull' || $this->method == 'increment' || $this->method == 'decrement' || $this->method == 'forever' || $this->method == 'forget' || $this->method == 'flush') {
            if($cacheArr['value'] === 'CACHE_BREAKDOWN' && $this->method === 'forever'){
                $cacheArr['time'] = $this->CACHE_BREAKDOWN_TIME;
                $retCache = $this->put($cacheArr, $object['before']);
            }else{
                $retCache = $this->{$this->method}($cacheArr);
            }
            if( $this->method == 'get'){
                if($retCache == 'EMPTY_CACHE'){
                    $index = $cacheArr;
                    $index['key'] = $cacheArr['key'].'_bat';
                    $index['time'] = 8000;
                    $retCache = $this->{$this->method}($index);
                    if($retCache == 'NO_CACHE'){
                        $retCache = 'ERROR_CACHE';
                    }
                }
            }
        } else {
            $retCache = 'NO_CACHE';
        }
        //其他
        if ($retCache !== 'NO_CACHE' && isset($object['action'][$this->method]) && $object['action'][$this->method]) {
            foreach ($object['action'][$this->method] as $key => $item) {
                $intemCache = new BaseCache();
                $intemCache->method = $item['method'];
                $intemCache->cacheName = $item['cacheName'];
                foreach ($item['cachePar'] as $key => $par) {
                    //返回值的key中获取
                    $par_index = trim($par, '$');
                    if ($par_index !== $par) {
                        if (isset($retCache[$par_index])) {
                            $par = $retCache[$par_index];
                        } else if (isset($retCache->$par_index)) {
                            $par = $retCache->$par_index;
                        } else {
                            $par = null;
                        }
                        $item['cachePar'][$key] = $par; //缓存tag
                        continue;
                    }
                    //#00#
                    for ($i = 0; $i < count($this->cachePar); $i++) {
                        $par = str_replace('#' . $i . $i . '#', $this->cachePar[$i], $par);
                    }
                    $item['cachePar'][$key] = $par; //缓存tag
                }
                $intemCache->cachePar = $item['cachePar']; //集合
                //value值--暂支持存储返回值的一级某个字段
                if(isset($item['cacheValue'])){
                    $cacheValueIndex = trim($item['cacheValue'], '$');
                    if ($cacheValueIndex !== $item['cacheValue']) {//返回值的key中获取
                        if (isset($retCache[$cacheValueIndex])) {
                            $item['cacheValue'] = $retCache[$cacheValueIndex];
                        } else if (isset($retCache->$cacheValueIndex)) {
                            $item['cacheValue'] = $retCache->$cacheValueIndex;
                        } else {
                            $item['cacheValue'] = null;
                        }
                    }
                    $intemCache->cacheValue = $item['cacheValue'];
                }
                $where = TRUE;
                if(isset($item['where'])){
                    foreach ($item['where'] as $key => $where) {
                        $key_len_half = floor(strlen(trim($key,'#'))/2);
                        if(!$key_len_half){
                            continue;
                        }
                        $key = substr(trim($key,'#'),0,$key_len_half);
                        if(isset($this->cachePar[$key]) && $where == $this->cachePar[$key]){
                            continue;
                        }else{
                            $where = FALSE;
                            break;
                        }
                    }
                }
                if($where){
                    $retIntemCache = $intemCache->HandleCache();
                }
                if (isset($item['cacheReturn']) && $item['cacheReturn']) {
                    foreach ($item['cacheReturn'] as $key => $value) {
                        if (isset($retCache[$value])) {
                            $retCache[$value] = $retIntemCache;
                        } else if (isset($retCache->$value)) {
                            $retCache->$value = $retIntemCache;
                        }
                    }
                }
            }
        }
        return $retCache;
    }

    /**
     * 是否有缓存
     */
    public function has($cacheArr, $before = 0) {
        return $this->get($cacheArr, $before);
    }

    /**
     * 获取缓存
     */
    public function get($cacheArr, $before = 0) {
        if (!isset($cacheArr['key'])) {
            return 'NO_CACHE';
        }
        foreach ($cacheArr as $key1 => $value1) {
            if ($key1 == 'tag') {
                if (is_array($value1)) {
                    foreach ($value1 as $key2 => $value2) {
                        $cacheArr[$key1][$key2] = $this->USER_PRE . $value2;
                    }
                } else {
                    $cacheArr[$key1] = $this->USER_PRE . $value1;
                }
            } else if ($key1 == 'key') {
                $cacheArr[$key1] = $this->USER_PRE . $value1;
            }
        }
        if (isset($cacheArr['tag']) && $cacheArr['tag'] && env('CACHE_DRIVER', 'file') === 'memcached') {
            if (Cache::tags($cacheArr['tag'])->has($cacheArr['key'])) {
                $cacheObject = Cache::tags($cacheArr['tag'])->get($cacheArr['key']);
                if (isset($cacheObject['value']) && isset($cacheObject['time'])) {
                    if (time() - $cacheObject['time'] + $before > 0) {
                        return 'NO_CACHE';
                    }
                    $cacheObject = $cacheObject['value'];
                }
                if ($cacheObject == 'NULL_CACHE') {
                    $cacheObject = null;
                }
                return $cacheObject;
            }
        } else {
            if (Cache::has($cacheArr['key'])) {
                $cacheObject = Cache::get($cacheArr['key']);
                if (isset($cacheObject['value']) && isset($cacheObject['time'])) {
                    if (time() - $cacheObject['time'] + $before > 0) {
                        return 'NO_CACHE';
                    }
                    $cacheObject = $cacheObject['value'];
                }
                if ($cacheObject == 'NULL_CACHE') {
                    $cacheObject = null;
                }
                return $cacheObject;
            }
        }
        return 'NO_CACHE';
    }

    /**
     * 存缓存
     */
    public function put($cacheArr, $before = 0) {
        if (!isset($cacheArr['key'])) {
            return;
        }
        foreach ($cacheArr as $key1 => $value1) {
            if ($key1 == 'tag') {
                if (is_array($value1)) {
                    foreach ($value1 as $key2 => $value2) {
                        $cacheArr[$key1][$key2] = $this->USER_PRE . $value2;
                    }
                } else {
                    $cacheArr[$key1] = $this->USER_PRE . $value1;
                }
            } else if ($key1 == 'key') {
                $cacheArr[$key1] = $this->USER_PRE . $value1;
            }
        }
        if ($cacheArr['value'] === null) {
            $cacheArr['value'] = 'NULL_CACHE';
        }
        if ($cacheArr['value'] === 'CACHE_BREAKDOWN') {
            $cacheArr['time'] = $this->CACHE_BREAKDOWN_TIME;
        }
        if ($before) {
            $cacheArr['value'] = array('value' => $cacheArr['value'], 'time' => time() + $cacheArr['time'] * 60);
        }
        if (isset($cacheArr['tag'])  && $cacheArr['tag'] && env('CACHE_DRIVER', 'file') === 'memcached') {
            if (Cache::tags($cacheArr['tag'])->has($cacheArr['key'])) {
                $ret = Cache::tags($cacheArr['tag'])->put($cacheArr['key'], $cacheArr['value'], $cacheArr['time']);
            } else {
                $ret = Cache::tags($cacheArr['tag'])->add($cacheArr['key'], $cacheArr['value'], $cacheArr['time']);
            }
        } else {
            if (Cache::has($cacheArr['key'])) {
                $ret = Cache::put($cacheArr['key'], $cacheArr['value'], $cacheArr['time']);
            } else {
                $ret = Cache::add($cacheArr['key'], $cacheArr['value'], $cacheArr['time']);
            }
        }
        $this->addCacheKeyArr($cacheArr);
        return $ret;
    }

    /**
     * pull缓存
     */
    public function pull($cacheArr, $before = 0) {
        if (!isset($cacheArr['key'])) {
            return 'NO_CACHE';
        }
        foreach ($cacheArr as $key1 => $value1) {
            if ($key1 == 'tag') {
                if (is_array($value1)) {
                    foreach ($value1 as $key2 => $value2) {
                        $cacheArr[$key1][$key2] = $this->USER_PRE . $value2;
                    }
                } else {
                    $cacheArr[$key1] = $this->USER_PRE . $value1;
                }
            } else if ($key1 == 'key') {
                $cacheArr[$key1] = $this->USER_PRE . $value1;
            }
        }
        if (isset($cacheArr['tag'])  && $cacheArr['tag']  && env('CACHE_DRIVER', 'file') === 'memcached') {
            if (Cache::tags($cacheArr['tag'])->has($cacheArr['key'])) {
                $cacheObject = Cache::tags($cacheArr['tag'])->pull($cacheArr['key']);
                if (isset($cacheObject['value']) && isset($cacheObject['time'])) {
                    $cacheObject = $cacheObject['value'];
                }
                return $cacheObject;
            }
        } else {
            if (Cache::has($cacheArr['key'])) {
                $cacheObject = Cache::pull($cacheArr['key']);
                if (isset($cacheObject['value']) && isset($cacheObject['time'])) {
                    $cacheObject = $cacheObject['value'];
                }
                return $cacheObject;
            }
        }
        return 'NO_CACHE';
    }

    /*
     * 缓存自增
     */
    public function increment($cacheArr, $before = 0) {
        if (!isset($cacheArr['key'])) {
            return;
        }
        foreach ($cacheArr as $key1 => $value1) {
            if ($key1 == 'tag') {
                if (is_array($value1)) {
                    foreach ($value1 as $key2 => $value2) {
                        $cacheArr[$key1][$key2] = $this->USER_PRE . $value2;
                    }
                } else {
                    $cacheArr[$key1] = $this->USER_PRE . $value1;
                }
            } else if ($key1 == 'key') {
                $cacheArr[$key1] = $this->USER_PRE . $value1;
            }
        }
        if($this->has($cacheArr) == 'NO_CACHE'){
            $cacheArr['value'] = 0;
            $this->put($cacheArr);
        }
        if (isset($cacheArr['tag'])  && $cacheArr['tag']  && env('CACHE_DRIVER', 'file') === 'memcached') {
            $ret = Cache::tags($cacheArr['tag'])->increment($cacheArr['key']);
        } else {
            $ret = Cache::increment($cacheArr['key']);
        }
        return $ret;
    }

    /*
     * 缓存递减
     */
    public function decrement($cacheArr, $before = 0) {
        if (!isset($cacheArr['key'])) {
            return;
        }
        foreach ($cacheArr as $key1 => $value1) {
            if ($key1 == 'tag') {
                if (is_array($value1)) {
                    foreach ($value1 as $key2 => $value2) {
                        $cacheArr[$key1][$key2] = $this->USER_PRE . $value2;
                    }
                } else {
                    $cacheArr[$key1] = $this->USER_PRE . $value1;
                }
            } else if ($key1 == 'key') {
                $cacheArr[$key1] = $this->USER_PRE . $value1;
            }
        }
        if (isset($cacheArr['tag']) && $cacheArr['tag']  && env('CACHE_DRIVER', 'file') === 'memcached') {
            $ret = Cache::tags($cacheArr['tag'])->decrement($cacheArr['key']);
        } else {
            $ret = Cache::decrement($cacheArr['key']);
        }
        return $ret;
    }

    /**
     * 存forever缓存
     */
    public function forever($cacheArr, $before = 0) {
        if (!isset($cacheArr['key'])) {
            return;
        }
        foreach ($cacheArr as $key1 => $value1) {
            if ($key1 == 'tag') {
                if (is_array($value1)) {
                    foreach ($value1 as $key2 => $value2) {
                        $cacheArr[$key1][$key2] = $this->USER_PRE . $value2;
                    }
                } else {
                    $cacheArr[$key1] = $this->USER_PRE . $value1;
                }
            } else if ($key1 == 'key') {
                $cacheArr[$key1] = $this->USER_PRE . $value1;
            }
        }
        if (isset($cacheArr['tag']) && $cacheArr['tag'] && env('CACHE_DRIVER', 'file') === 'memcached') {
            $ret = Cache::tags($cacheArr['tag'])->forever($cacheArr['key'], $cacheArr['value']);
        } else {
            $ret = Cache::forever($cacheArr['key'], $cacheArr['value']);
        }
        $cacheArr['time'] = 'forever';
        $this->addCacheKeyArr($cacheArr);
        return $ret;
    }

    /*
     * 忘记缓存
     */
    public function forget($info, $before = 0) {
        foreach ($info as $key1 => $value1) {
            if ($key1 == 'tag') {
                if (is_array($value1)) {
                    foreach ($value1 as $key2 => $value2) {
                        $info[$key1][$key2] = $this->USER_PRE . $value2;
                    }
                } else {
                    $info[$key1] = $this->USER_PRE . $value1;
                }
            } else if ($key1 == 'key') {
                $info[$key1] = $this->USER_PRE . $value1;
            }
        }
//		delCacheKeyArr($info);
        if (env('CACHE_DRIVER', 'file') === 'memcached' && isset($info['tag']) && isset($info['key']) && $info['tag'] && $info['key']) {
            Cache::tags($info['tag'])->forget($info['key']);
            return TRUE;
        } else if (env('CACHE_DRIVER', 'file') === 'memcached' && isset($info['tag']) && $info['tag'] && (!isset($info['key']) || !$info['key'])) {
            Cache::tags($info['tag'])->flush();
            return TRUE;
        } else if (isset($info['key']) && $info['key']) {
            Cache::forget($info['key']);
            return TRUE;
        } else {
            return FALSE;
        }
    }

    /*
     * 忘记缓存tag
     */
    
    public function flush($info, $before = 0) {
        foreach ($info as $key1 => $value1) {
            if ($key1 == 'tag') {
                if (is_array($value1)) {
                    foreach ($value1 as $key2 => $value2) {
                        $info[$key1][$key2] = $this->USER_PRE . $value2;
                    }
                } else {
                    $info[$key1] = $this->USER_PRE . $value1;
                }
            }
        }
//		delCacheKeyArr($info);
        if (env('CACHE_DRIVER', 'file') === 'memcached' && isset($info['tag']) && $info['tag']) {
            Cache::tags($info['tag'])->flush();
            return TRUE;
        } else {
            return FALSE;
        }
    }

    /*
     * 加入可用的缓存tag和key
     */
    public function addCacheKeyArr($cacheArr) {
        if (!isset($cacheArr['key'])) {
            return;
        }
        if(isset($cacheArr['tag'][0]) && $cacheArr['tag'][0]){
            $key  = $cacheArr['tag'][0];
        }else if($cacheArr['tag']){
            $key  = $cacheArr['tag'];
        }else{
            $key  = 'NOTAG';
        }
        $cachetemp = array();
        $cacheObject = array(); 
        if (Cache::has($key)) {
            $cacheObject = Cache::get($key);
        } else {
            $cacheObject['time'] = time();
        }
        if (isset($cacheArr['tag']) && env('CACHE_DRIVER', 'file') === 'memcached') {
            $cachetemp = array(
                'tag' => $cacheArr['tag'],
                'key' => $cacheArr['key'],
                't_out' => $cacheArr['time'], //超时时间(分) || forver
                't_at' => time(), //存缓存时间(s)
                'v' => '1', //1-可用，0-不可用
            );
            $cacheObject[$cacheArr['key']] = $cachetemp;
            Cache::forever($key, $cacheObject);
        } else {
            $cachetemp = array(
                'tag' => '',
                'key' => $cacheArr['key'],
                't_out' => $cacheArr['time'],
                't_at' => time(),
                'v' => '1',
            );
            $cacheObject[$cacheArr['key']] = $cachetemp;
            Cache::forever($key, $cacheObject);
        }
    }
    
    /*
     * 删除的缓存tag和key
     */
    public function delCacheKeyArr($info) {
        $cacheObject = array();
        if (Cache::has($this->CACHEKEY)) {
            $cacheObject = Cache::get($this->CACHEKEY);
            Cache::forget($this->CACHEKEY);
        }
        if (!empty($cacheObject)) {
            if (env('CACHE_DRIVER', 'file') === 'memcached' && isset($info['cacheTag']) && isset($info['cacheKey']) && $info['cacheTag'] && $info['cacheKey']) {
                foreach ($cacheObject as $key => $val) {
                    if ($val['tag'] == $info['cacheTag'] && $val['key'] == $info['cacheKey']) {
                        $val['v'] = '0';
                        unset($cacheObject[$key]);
                    }
                }
                Cache::forever($this->CACHEKEY, $cacheObject);
                return TRUE;
            } else if (env('CACHE_DRIVER', 'file') === 'memcached' && $info['cacheTag'] && (!isset($info['cacheKey']) || !$info['cacheKey'])) {
                foreach ($cacheObject as $key => $val) {
                    if ($val['tag'] == $info['cacheTag']) {
                        $val['v'] = '0';
                        unset($cacheObject[$key]);
                    }
                }
                Cache::forever($this->CACHEKEY, $cacheObject);
                return TRUE;
            } else if (isset($info['cacheKey']) && $info['cacheKey']) {
                foreach ($cacheObject as $key => $val) {
                    if ($val['key'] == $info['cacheKey']) {
                        $val['v'] = '0';
                        unset($cacheObject[$key]);
                    }
                }
                Cache::forever($this->CACHEKEY, $cacheObject);
                return TRUE;
            } else {
                return FALSE;
            }
        } else {
            return FALSE;
        }
    }

}
